#include<stdio.h>
#include <sys/types.h>  //int socket(int domain, int type, int protocol);
#include <sys/socket.h>
#include <errno.h>  //perror
#include <netinet/in.h> //struct sockaddr_in;
#include <string.h> //memset
#include <arpa/inet.h>  //htonl
#include <unistd.h> //close
#include <stdlib.h> //malloc()
#include <time.h>  //time()

//====macro define====
#define SERVER_PORT 554
#define SERVER_RTP_PORT  55532
#define SERVER_RTCP_PORT 55533

//====Functional functions====

/*

*/
int handleCmd_OPTIONS(char*result,int cseq)
{
     sprintf(result, "RTSP/1.0 200 OK\r\n"
        "CSeq: %d\r\n"
        "Public: OPTIONS, DESCRIBE, SETUP, PLAY\r\n"
        "\r\n",
        cseq);

    return 0;
}
/*
主要构成dsp信息
*/
int handleCmd_DESCRIBE(char*result,int cseq,char*url)
{
    char sdp[500];
    char localIp[100];

    sscanf(url, "rtsp://%[^:]:", localIp);

    sprintf(sdp, "v=0\r\n"
        "o=- 9%ld 1 IN IP4 %s\r\n"
        "t=0 0\r\n"
        "a=control:*\r\n"
        "m=video 0 RTP/AVP 96\r\n"
        "a=rtpmap:96 H264/90000\r\n"
        "a=control:track0\r\n",
        time(NULL), localIp);

    sprintf(result, "RTSP/1.0 200 OK\r\nCSeq: %d\r\n"
        "Content-Base: %s\r\n"
        "Content-type: application/sdp\r\n"
        "Content-length: %zu\r\n\r\n"
        "%s",
        cseq,
        url,
        strlen(sdp),
        sdp);

    return 0;
}
/*

*/
int handleCmd_SETUP(char* result, int cseq, int clientRtpPort)
{
    sprintf(result, "RTSP/1.0 200 OK\r\n"
        "CSeq: %d\r\n"
        "Transport: RTP/AVP;unicast;client_port=%d-%d;server_port=%d-%d\r\n"
        "Session: 66334873\r\n"
        "\r\n",
        cseq,
        clientRtpPort,
        clientRtpPort + 1,
        SERVER_RTP_PORT,
        SERVER_RTCP_PORT);

    return 0;
}
int handleCmd_PLAY(char* result,int cseq)
{
     sprintf(result, "RTSP/1.0 200 OK\r\n"
        "CSeq: %d\r\n"
        "Range: npt=0.000-\r\n"
        "Session: 66334873; timeout=10\r\n\r\n",
        cseq);

    return 0;
}



/*  1：循环执行->接受请求+发送数据应答；
    2：
*/
void doClient(int extablished_fd,struct sockaddr_in addr_from_client)
{
//1.Local variables
    int revlen = -1;
    char *Rbuf =  (char*)malloc(sizeof(char)*2000);   //一般1500就够了 。MTU 
    char *Recvstr = (char*)malloc(sizeof(char)*2000);
    char *sbuf = (char*)malloc(sizeof(char)*2000);
    char * line;
    char method[40];
    char url[100];
    char version[40];
    int CSeq;
    int clinetrtpport,clientrtcpport;
//2：接受请求信息
    while(1){
//每次循环前应该清空数据的        
        memset(Rbuf,0,2000);
        memset(sbuf,0,2000);
        memset(method,0,sizeof(method)/sizeof(char));
        memset(url,0,sizeof(url)/sizeof(char));
        CSeq = 0;

        revlen = recv(extablished_fd,Rbuf,1999,0);  //ssize_t recv(int sockfd, void *buf, size_t len, int flags);
        if(revlen<0)
        {
            perror("recv failed:");
            continue;
        }
        Rbuf[revlen] = '\0';
        strcpy(Recvstr,Rbuf);   //保存起来，后面查找分割的函数会破坏字符串。

        printf("start ques >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
        printf("recvie from clien = %s\n",Rbuf);
        printf("end ques >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
/*3：请求的格式解析    
https://blog.csdn.net/m0_48578207/article/details/126493691
方法+url+rtsp版本+CR LF（回车+换行）
消息头+CR LF
CR LF
消息体+CR LF
*/
        const char *seq = "\n";
        line = strtok(Rbuf,seq);  //char *strtok(char *str, const char *delim),找不到返回null
        //需要找出请求的方法；报文序号；客户端rtp,rtcp 的端口号。
        while(line)
        {
            /*line OPTIONS rtsp://127.0.0.1:8554 RTSP/1.0;DESCRIBE rtsp://127.0.0.1:8554 RTSP/1.0;SETUP rtsp://127.0.0.1:8554/track0 RTSP/1.0;
                看服务端端提供什么操作：OPTIONS, DESCRIBE, SETUP, PLAY
            */
           if(strstr(line,"OPTIONS")||
           strstr(line,"DESCRIBE")||
           strstr(line,"SETUP")||
           strstr(line,"PLAY"))
           {
            if(sscanf(line,"%s %s %s\r\n",method,url,version)!=3){
                //error
            }
           }
           else if(strstr(line,"CSeq"))
           {
            if(sscanf(line,"CSeq: %d\r\n",&CSeq)!=1)
            {
                //error
            }
           }
           else if(!strncmp(line,"Transport:",sizeof("Transport:")))
           {
            if(sscanf(line,"Transport: RTP/AVP;unicast;client_port=%d-%d\r\n",&clinetrtpport,&clientrtcpport)!=2)
            {
                //error
            }
           }

           line = strtok(NULL,seq);
        }

//4：根据不同的请求构造相关的回答报文,如果请求不能构成报文，推出循环。
        if(!strcmp(method,"OPTIONS"))
        {
            if(handleCmd_OPTIONS(sbuf,CSeq))
            {
                printf("failed to handle OPTIONS\n");
            }

        }
        else if(!strcmp(method,"DESCRIBE"))
        {
            if(handleCmd_DESCRIBE(sbuf,CSeq,url))
            {
                printf("failed to handle DESCRIBE\n");
            }

        }
        else if(!strcmp(method,"SETUP"))
        {
            if(handleCmd_SETUP(sbuf,CSeq,clinetrtpport))
            {
                printf("failed to handle SETUP\n");
            }
        }
        else if(!strcmp(method,"PLAY"))
        {
            if (handleCmd_PLAY(sbuf, CSeq))
            {
                printf("failed to handle play\n");
                break;
            }

        }
        else
        {
              printf("method=%s not support! \n",method);
        }
        printf("method=%s\n",method);
//5：发送构造好的数据报给客户端
        printf("sbuf=%s\n",sbuf);

        send(extablished_fd,sbuf,strlen(sbuf),0);  //发送构造好的应答数据包
//6：当请求是play 播放的时候，就一直循环等待？ 哦噢，真实情况应该是提供播放的视频源。
    if(!strcmp(method,"PLAY"))
    {
        printf("start play !\n clientip=%d,clientpor!t=%d",addr_from_client.sin_addr.s_addr,addr_from_client.sin_port);
        while(1)
        {          
            sleep(10);
        }

        break; //跳出循环接收信息的循环。
    }


    }
//7:程序推出，连接套接字，释放堆内存。
    close(extablished_fd);
    free(Rbuf);
    free(sbuf);
    free(Recvstr);

}


/*====主程序====
1：建立IPV4/TCP的套接字，listen 客户端
2:循环执行accept（建立连接，循环就是重新等待建立链接）。
3：accept后（建立连接后），执行请求。
*/
int main() {
//1.Local variables
    int sockfd = -1;
    struct sockaddr_in client_addr;
    memset(&client_addr,0,sizeof(client_addr));
    client_addr.sin_port = htons(SERVER_PORT);
    client_addr.sin_family = AF_INET;
    client_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  //  client_addr.sin_addr.s_addr = inet_addr("192.168.31.148");

    int clientSockfd; //建立连接套接字
    struct sockaddr_in addr_form_cli;
    socklen_t addr_form_cli_len;

 //   int s32Socket_opt_value = 1;



//2：创建套接字
    sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0)
    {
        perror("socket failed:");
        return -1;
    }
    //==
  //  if (setsockopt(sockfd ,SOL_SOCKET,SO_REUSEADDR,&s32Socket_opt_value,sizeof(int)) == -1) //允许地址重用？    
   // {
  //      return -1;
  //  }
    
 //   int s32Socket_opt_value = 1;  
//if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &s32Socket_opt_value, sizeof(int)) == -1) {  
  //  return -1;  
//}


    //====
    if(bind(sockfd,(struct sockaddr*)&client_addr,sizeof(client_addr))==-1)
    {
        perror("bind failed:");
        close(sockfd);
        return -1;
    }
    if(listen(sockfd,5)==-1)
    {
        perror("listen failed:");
        close(sockfd);
        return -1;
    }

  //  printf("%s rtsp://127.0.0.1:%d\n", __FILE__, SERVER_PORT);

//3.接收客户端连接。连接（tcp 三次握手）获得连接套接字
    while(1)
    {
        memset(&addr_form_cli,0,sizeof(addr_form_cli));
        clientSockfd = accept(sockfd,(struct sockaddr*)&addr_form_cli,&addr_form_cli_len);
        if(clientSockfd<0)
        {
            perror("accept failed:");
            continue;
        }
        //处理请求，这里只是简单的打印客户端地址信息。
        printf("Received connection from %s:%d\n",inet_ntoa(addr_form_cli.sin_addr),ntohs(addr_form_cli.sin_port));

//建立连接后，处理请求。
        doClient(clientSockfd,addr_form_cli);
    }

    close(sockfd);

    return 0;
}
